namespace Xmef.Engine
{
    using System;
    using Xmef.Interfaces;
    using Xmef.Library;
    using Xmef.Serializer;

    [Serializable]
    public class PropertyCollection : PluginIdentity, IContainer<Property>, ICompatible<PropertyCollection>
    {
        private PropertyCollection<Property> _dictionary = new PropertyCollection<Property>();
        public static readonly IXmlRuntimeFormatter Formatter = new XmlFormatter();
        public static readonly IXmlStringSerializer<PropertyCollection> XmlSerializer = new XmlUnicodeStringSerializer<PropertyCollection>();

        public event PropertyCollectionItemEventHandler ItemAdded;
        public event PropertyCollectionItemEventHandler ItemChanged;
        public event PropertyCollectionItemEventHandler ItemRemoved;

        public bool Add(Property property)
        {
            try
            {
                this._dictionary.Add(property);
                if (this.ItemAdded != null)
                {
                    this.ItemAdded(property);
                }
                return true;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorSync(ex);
                return false;
            }
        }

        public void Assign(PropertyCollection obj)
        {
            this._dictionary = obj._dictionary;
        }

        public void Clear()
        {
            try
            {
                if (this._dictionary.Count > 0)
                {
                    PropertyCollection<Property> tmp = new PropertyCollection<Property>(this._dictionary);
                    this._dictionary.Clear();
                    foreach (Property property in tmp)
                    {
                        if (this.ItemRemoved != null)
                        {
                            this.ItemRemoved(property);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorSync(ex);
            }
        }

        private Property<T> GetProperty<T>(string propertyName)
        {
            return this.GetProperty<T>(propertyName, default(T));
        }

        private Property<T> GetProperty<T>(string propertyName, T defaultValue)
        {
            return this.GetProperty<T>(propertyName, defaultValue, PropertyConverter<T>.Default);
        }

        private Property<T> GetProperty<T>(string propertyName, T defaultValue, IPropertyConverter<T> converter)
        {
            try
            {
                Property property = this._dictionary[propertyName];
                if (property == null)
                {
                    property = new Property(typeof(Property<T>), propertyName, PropertyConverter.ConvertToString<T>(converter, defaultValue));
                    this._dictionary.Add(property);
                }
                return new Property<T>(property, defaultValue, converter);
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorSync(ex);
            }
            return null;
        }

        public T GetValue<T>(string propertyName)
        {
            return this.GetValue<T>(propertyName, default(T));
        }

        public T GetValue<T>(string propertyName, T defaultValue)
        {
            return this.GetValue<T>(propertyName, defaultValue, PropertyConverter<T>.Default);
        }

        public T GetValue<T>(string propertyName, T defaultValue, IPropertyConverter<T> converter)
        {
            if (converter != null)
            {
                IProperty<T> option = this.GetProperty<T>(propertyName, defaultValue, converter);
                if (option != null)
                {
                    T result = option.GetValue();
                    if (!object.Equals(result, defaultValue))
                    {
                        return result;
                    }
                }
            }
            return defaultValue;
        }

        public bool Remove(Property property)
        {
            try
            {
                this._dictionary.Remove(property);
                if (this.ItemRemoved != null)
                {
                    this.ItemRemoved(property);
                }
                return true;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorSync(ex);
                return false;
            }
        }

        public bool SetValue<T>(string propertyName, T value)
        {
            return this.SetValue<T>(propertyName, value, default(T));
        }

        public bool SetValue<T>(string propertyName, T value, T defaultValue)
        {
            return this.SetValue<T>(propertyName, value, defaultValue, PropertyConverter<T>.Default);
        }

        public bool SetValue<T>(string propertyName, T value, T defaultValue, IPropertyConverter<T> converter)
        {
            Property<T> property = this.GetProperty<T>(propertyName, defaultValue, converter);
            if (property != null)
            {
                if (object.Equals(value, property.DefaultValue))
                {
                    this._dictionary.Remove(property);
                }
                if (!property.Equals(value))
                {
                    property.SetValue(value);
                    if (this.ItemChanged != null)
                    {
                        this.ItemChanged(property);
                    }
                }
                return true;
            }
            return false;
        }

        public override string ToString()
        {
            return XmlSerializer.Serialize(this, Formatter);
        }

        bool IContainer<Property>.Contains(Property item)
        {
            return this._dictionary.Contains(item);
        }

        public int Count
        {
            get
            {
                return this._dictionary.Count;
            }
        }

        public PropertyCollection<Property> Properties
        {
            get
            {
                return this._dictionary;
            }
            set
            {
                this._dictionary = value;
            }
        }

        private class XmlFormatter : XmlTypeSerializer<PropertyCollection>
        {
            public override void AddOverrides(IXmlTypeFormatter formatter)
            {
                AddType(formatter, "properties");
                AddArray(formatter, "Properties", "properties");
                AddArrayItem(formatter, "Properties", "item", typeof(Property));
                Property.Formatter.AddOverrides(formatter);
            }
        }
    }
}

